<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
<meta name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">

    <meta name="author" content="Ian Tang">


    <meta name="subtitle" content="岁月不居时节如流">


    <meta name="description" content="好记性不如烂笔头">



<title>SDN架构与P4语言入门 | 随笔</title>



    <link rel="icon" href="/image/The_Lion_King_32_32.png">




    <!-- stylesheets list from _config.yml -->
    
    <link rel="stylesheet" href="/css/style.css">
    



    <!-- scripts list from _config.yml -->
    
    <script src="/js/script.js"></script>
    
    <script src="/js/tocbot.min.js"></script>
    



    
    
        
    


<meta name="generator" content="Hexo 6.3.0"></head>

<body>
    <script>
        // this function is used to check current theme before page loaded.
        (() => {
            const currentTheme = window.localStorage && window.localStorage.getItem('theme') || '';
            const isDark = currentTheme === 'dark';
            const pagebody = document.getElementsByTagName('body')[0]
            if (isDark) {
                pagebody.classList.add('dark-theme');
                // mobile
                document.getElementById("mobile-toggle-theme").innerText = "· Dark"
            } else {
                pagebody.classList.remove('dark-theme');
                // mobile
                document.getElementById("mobile-toggle-theme").innerText = "· Light"
            }
        })();
    </script>

    <div class="wrapper">
        <header>
    <nav class="navbar">
        <div class="container">
            <div class="navbar-header header-logo"><a href="/">Tangkaifei&#39;s Blog</a></div>
            <div class="menu navbar-right">
                
                    <a class="menu-item" href="/archives">Posts</a>
                
                    <a class="menu-item" href="/category">Categories</a>
                
                    <a class="menu-item" href="/tag">Tags</a>
                
                    <a class="menu-item" href="/about">About</a>
                
                <input id="switch_default" type="checkbox" class="switch_default">
                <label for="switch_default" class="toggleBtn"></label>
            </div>
        </div>
    </nav>

    
    <nav class="navbar-mobile" id="nav-mobile">
        <div class="container">
            <div class="navbar-header">
                <div>
                    <a href="/">Tangkaifei&#39;s Blog</a><a id="mobile-toggle-theme">·&nbsp;Light</a>
                </div>
                <div class="menu-toggle" onclick="mobileBtn()">&#9776; Menu</div>
            </div>
            <div class="menu" id="mobile-menu">
                
                    <a class="menu-item" href="/archives">Posts</a>
                
                    <a class="menu-item" href="/category">Categories</a>
                
                    <a class="menu-item" href="/tag">Tags</a>
                
                    <a class="menu-item" href="/about">About</a>
                
            </div>
        </div>
    </nav>

</header>
<script>
    var mobileBtn = function f() {
        var toggleMenu = document.getElementsByClassName("menu-toggle")[0];
        var mobileMenu = document.getElementById("mobile-menu");
        if(toggleMenu.classList.contains("active")){
           toggleMenu.classList.remove("active")
            mobileMenu.classList.remove("active")
        }else{
            toggleMenu.classList.add("active")
            mobileMenu.classList.add("active")
        }
    }
</script>
            <div class="main">
                <div class="container">
    
    
        <div class="post-toc">
    <div class="tocbot-list">
    </div>
    <div class="tocbot-list-menu">
        <a class="tocbot-toc-expand" onclick="expand_toc()">Expand all</a>
        <a onclick="go_top()">Back to top</a>
        <a onclick="go_bottom()">Go to bottom</a>
    </div>
</div>

<script>
    var tocbot_timer;
    var DEPTH_MAX = 6; // 为 6 时展开所有
    var tocbot_default_config = {
        tocSelector: '.tocbot-list',
        contentSelector: '.post-content',
        headingSelector: 'h1, h2, h3, h4, h5',
        orderedList: false,
        scrollSmooth: true,
        onClick: extend_click,
    };

    function extend_click() {
        clearTimeout(tocbot_timer);
        tocbot_timer = setTimeout(function() {
            tocbot.refresh(obj_merge(tocbot_default_config, {
                hasInnerContainers: true
            }));
        }, 420); // 这个值是由 tocbot 源码里定义的 scrollSmoothDuration 得来的
    }

    document.ready(function() {
        tocbot.init(obj_merge(tocbot_default_config, {
            collapseDepth: 1
        }));
    });

    function expand_toc() {
        var b = document.querySelector('.tocbot-toc-expand');
        var expanded = b.getAttribute('data-expanded');
        expanded ? b.removeAttribute('data-expanded') : b.setAttribute('data-expanded', true);
        tocbot.refresh(obj_merge(tocbot_default_config, {
            collapseDepth: expanded ? 1 : DEPTH_MAX
        }));
        b.innerText = expanded ? 'Expand all' : 'Collapse all';
    }

    function go_top() {
        window.scrollTo(0, 0);
    }

    function go_bottom() {
        window.scrollTo(0, document.body.scrollHeight);
    }

    function obj_merge(target, source) {
        for (var item in source) {
            if (source.hasOwnProperty(item)) {
                target[item] = source[item];
            }
        }
        return target;
    }
</script>
    

    
    <article class="post-wrap">
        <header class="post-header">
            <h1 class="post-title">SDN架构与P4语言入门</h1>
            
                <div class="post-meta">
                    
                        Author: <a itemprop="author" rel="author" href="/">Ian Tang</a>
                    

                    
                        <span class="post-time">
                        Date: <a href="#">十一月 27, 2022&nbsp;&nbsp;12:20:00</a>
                        </span>
                    
                    
                        <span class="post-category">
                    Category:
                            
                                <a href="/categories/%E5%BA%94%E7%94%A8%EF%BC%9A%E5%BA%94%E7%94%A8%E7%AC%94%E8%AE%B0/">应用：应用笔记</a>
                            
                        </span>
                    
                </div>
            
        </header>

        <div class="post-content">
            <h1 id="sdn"><a href="#sdn" class="headerlink" title="sdn"></a>sdn</h1><p><strong>SDN</strong>是一种思想，不是任何协议或者技术，是一种把专用电信硬件采用x86通用硬件替代的思想，专用电信硬件所支持的各种协议，通过sdn思想，软件编程化，即软件定义网络（soft defined network）。</p>
<p>只要网络硬件可以集中软件式管理、可编程、转发和控制分开，则认为这个网络是一个sdn网络。</p>
<p>作者：int32bit<br>链接：<a target="_blank" rel="noopener" href="https://www.zhihu.com/question/37126320/answer/382480862">https://www.zhihu.com/question/37126320/answer/382480862</a><br>来源：知乎<br>著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。</p>
<p>首先从大的角度谈区别，OpenStack是一个IaaS开源实现方案之一，提供计算、存储、网络等基础服务以及诸如数据库服务、大数据服务、容器编排服务等高级服务，而SDN主要聚集在网络这一块。因此从技术生态覆盖面角度看，OpenStack的生态方方面面十分庞大，网络只是其中的一个点，由组件Neutron负责实现。而SDN的生态只有网络这一点。</p>
<p>同样是网络，<strong>OpenStack Neutron注重的是为虚拟机、容器、物理服务器等提供网络服务</strong>，提供的是面向用户的北向RESTFul API，而<strong>SDN注重的是解决网络设备的配置以及转发控制问题</strong>，更侧重于底层网络设备的配置管理以及控制，既要提供面向用户的北向API，还要提供南向API，对接各种硬件设备的协议。</p>
<p>更细化的角度看联系，SDN的思想是控制转发分离，这是一种网络设计理念和变革。具体什么是控制转发分离，google有很专业的描述。我的简单理解就是把以前需要手动ssh或者通过console一台一台配置交换机、路由器、防火墙、负载均衡等硬件的活，现在通过一个中心控制器统一纳管起来，对用户屏蔽了底层硬件区别，网络管理员只需要调用标准API或者Web界面操作控制器就能自动下发配置策略到指定的设备中。当然除了配置问题，有些功能也从网络设备中剥离出来，放到控制器实现，不再揉到一块，这些功能包括链路发现、mac地址学习、路由计算等。硬件只负责转发或者路由即可。控制器如何与硬件打交道呢？当然是通过协议了，如openflow、netconf等。这里我们只关注SDN中的控制器，显然这是一个调度与控制中心，向上对用户负责，为用户提供API，向下对接各种网络设备，下发策略。</p>
<p>我们再来看OpenStack Neutron，我们知道，OpenStack Neutron由neutron-server和一堆agents&#x2F;drivers组成。这个neutron-server的功能和SDN中的控制器非常类似，向上对用户负责，为用户提供可编程API，向下对接各种agents，只是对接的协议不同，neutron-server与agent的交互的南向协议是RPC。所以我们可以简单理解neutron-server其实就是SDN控制器实现之一（不一定准确），负责向各种agents下发策略，agents再向各种网络设备转发策略。问题是除了虚拟网络设备（如OVS），还有各种各样的硬件网络设备，这些网络设备的功能、配置标准以及协议等均存在很大的差别，如果OpenStack Neutron要全部纳管起来，需要写一堆的agents和drivers，而且是重复造轮子，功能上也没有解耦，Neutron会非常重。最好的方式是不要直接去对接硬件，直接对接现成的SDN控制器即可，因为SDN控制器提供的北向API通常是标准的，而又屏蔽了底层硬件设备的差异化，不再需要实现各种各种的agents和drivers了，只需要实现对接SDN控制器的agent或者driver即可，比如对接开源的SDN控制器ODL(<a href="https://link.zhihu.com/?target=https://github.com/openstack/networking-odl">openstack&#x2F;networking-odl</a>)以及华为AC控制器等等。</p>
<p>简而言之，SDN是一种思想，核心理念是控制与转发分离，控制由控制器实现，转发由网络设备实现，其中ODL是控制器的开源实现之一。OpenStack Neutron是为OpenStack提供网络服务的组件，neutron-server服务可以当作是SDN控制器的实现之一，Neutorn可以串接其它的SDN控制器，比如ODL。</p>
<p>另外，如果想从事SDN或者OpenStack开发，并不是一定要两者都懂，看你负责什么工作了。OpenStack的大多数开发都不需要直接接触SDN，比如你负责存储服务Cinder或者计算服务Nova，基本就不会直接与SDN打交道。同样的，从事SDN也不一定非要和OpenStack扯上关系，二者可以说是完全解耦独立的。当然，如果你负责OpenStack Neutron开发，并且需要对接SDN，则对二者的熟悉是必须的了。</p>
<p>P4是一门封包处理器描述语言：</p>
<ul>
<li><p>无协议protocol限制</p>
<p>可以很有弹性的处理任何协议</p>
</li>
<li><p>无目标target限制</p>
<p>可以描述不同的目标，从高速的ASIC到软件交换机</p>
</li>
<li><p>可重新组态的网络</p>
<p>可以在部署好网络之后再重新定义</p>
</li>
</ul>
<p><img src="https://pic.downk.cc/item/5f4ceb4b160a154a67f53ef6.jpg"></p>
<p>P4项目由很多个单独的模块组成，每个模块就是一个子项目，下面分别简单介绍一下各模块的功能。</p>
<h1 id="SDN南向编程接口"><a href="#SDN南向编程接口" class="headerlink" title="SDN南向编程接口"></a>SDN南向编程接口</h1><h2 id="狭义SDN南向编程接口"><a href="#狭义SDN南向编程接口" class="headerlink" title="狭义SDN南向编程接口"></a>狭义SDN南向编程接口</h2><h3 id="OpenFlow原理"><a href="#OpenFlow原理" class="headerlink" title="OpenFlow原理"></a><strong>OpenFlow原理</strong></h3><p>OpenFlow交换机包括安全通道和流表两部分，流表负责存放流表项；安全通道负责与控制器的通信，可以建立在tcp上，也可以给予TLS加密之后的socket建立。</p>
<p>后续在1.3版本增加了组表和Meter表。</p>
<p>具体流程是：</p>
<ol>
<li>交换机将自身的状态和端口描述等信息上报给控制器</li>
<li>当有数据通过交换机传输进来时，交换机根据流表项决定怎么处理数据</li>
<li>如果流表项没有匹配到，将数据包放入packet-in报文中上报到控制器，控制器接收到packet-in报文，可以选择下发流表项和下发packet-out报文给交换机告知怎么处理数据流。交换机只是一个策略执行者</li>
</ol>
<h3 id="OpenFlow表"><a href="#OpenFlow表" class="headerlink" title="OpenFlow表"></a><strong>OpenFlow表</strong></h3><ol>
<li><p>流表</p>
<p>Flow Table是交换机存放流表项，多级流表将数据包的处理分解为多个子逻辑，用多张流表来匹配和处理，对数据包的处理编程了流水线。</p>
<p>流表项由<strong>匹配域、指令集和计数器</strong>三个主要部分和其他部分组成。</p>
<ul>
<li><p>匹配域用于区分不同的数据流</p>
<p><font color=red>数据流是指数据包进入交换机后匹配流表项，匹配到同一个流表的数据包称为数据流，即Data flow，简写flow</font></p>
</li>
<li><p>指令集用于决定匹配到的数据流动作</p>
</li>
<li><p>计数器用于统计匹配到流表的数据包数量和自己输等</p>
</li>
</ul>
</li>
<li><p>组表</p>
<p>Group Table用于定于一组动作，方便流表指令集复用，从而使用组播、负载均衡、容灾备份和聚合。</p>
<p>比如交换机需要将发送到端口1的标签都弹出VLAN标签，则可以创建一个ALL类型的动作桶，然后将弹出VLAN动作和转发到端口1放入其中，这样所有需要转发到端口1的流表项的动作集之需要跳转到对应的组表项，然后执行组表项内全部的动作桶动作。</p>
<p>组表存在降低流表的逻辑复杂度和减少存储空间</p>
</li>
<li><p>Meter表</p>
<p>Meter Table用于计量和限速。可以针对流制定对应的限速等规则</p>
</li>
</ol>
<h3 id="OpenFlow通道"><a href="#OpenFlow通道" class="headerlink" title="OpenFlow通道"></a><strong>OpenFlow通道</strong></h3><p>安全通道是交换机与控制器通信的通道，通道转发的数据为OpenFlow消息&#x2F;报文。OpenFlow报文为Controller-to-Switch、Asynchronous和Symmetric三大类。Controller-to-Swtich是控制器初始化发送给交换机，Asynchronous是交换机异步上报给控制器的报文、Symmetric是无须等待对方请求，双发都可以任意发送的报文。</p>
<ol>
<li>Controller-to-Switch报文类型<ul>
<li>Features：控制器主动发送Feature_request报文，要求交换机回复特性信息，报文只有数据报头（Head），没有消息体（body）。交换机在收到Feature_request报文后通过Feature_reply报文回复交换机的特性和交换机端口的特性。</li>
<li>Configuration：包含请求、回复、设置三种报文。控制器可以设置和请求交换机的配置信息，交换机则需执行配置和回复配置报文。</li>
<li>Modify-State：修改状态型报文，用于修改交换机的流表、组表、Meter表及端口状态</li>
<li>Read-State：读取状态信息，用于获取交换机的状态信息，包括流表、组表、Meter表及端口状态</li>
<li>Packet-out：响应Packet-in报文</li>
<li>Barrier</li>
<li>Role-Request</li>
<li>Asynchronous-Configuration</li>
</ul>
</li>
<li>Asynchronous</li>
<li>Symmetric</li>
</ol>
<h2 id="广义SDN南向编程接口"><a href="#广义SDN南向编程接口" class="headerlink" title="广义SDN南向编程接口"></a>广义SDN南向编程接口</h2><p>OF-config</p>
<p>OVSdb</p>
<p>NETCONF</p>
<p>XMPP</p>
<p>PCEP</p>
<p>OpFlex</p>
<h2 id="完全可编程南向编程接口"><a href="#完全可编程南向编程接口" class="headerlink" title="完全可编程南向编程接口"></a>完全可编程南向编程接口</h2><h3 id="POF"><a href="#POF" class="headerlink" title="POF"></a>POF</h3><p>华为提出的采用{offset,length}来定位数据，执行操作，取代了OpenFlow匹配域的限制，对于新协议支持更加灵活</p>
<h3 id="P4"><a href="#P4" class="headerlink" title="P4"></a>P4</h3><p>Openflow只能在已经固化的交换机逻辑上通过流表项指导数据的处理，没有办法重新定义处理数据的逻辑，对于新协议的支持缺乏灵活性。</p>
<p><img src="https://pic.downk.cc/item/5f0bff5214195aa5946bb3da.png"></p>
<h4 id="交换机结构"><a href="#交换机结构" class="headerlink" title="交换机结构"></a>交换机结构</h4><p>在传统交换机中，数据流转化为数据帧之后进行解析，首先检查的是tag，包括有无tag、灵活Q-in-Q、VLAN映射等。VLAN tag的检查和处理在所有厂商的交换机中都是必须的，但随后数据包处理流程就因厂商而异了，不同厂商的芯片设计会产生不同的处理流程，每个处理流程就是一个基础的数据处理单元，一般情况下，交换机的流水线包含6-8个数据处理单元。</p>
<p><img src="https://pic.downk.cc/item/5f0bffa514195aa5946bc9e9.png"></p>
<p>上图列出的是几个比较常见的数据帧处理单元，如二层转发、ACL转发等。流水线上每个基础数据处理单元处理数据的过程，就是根据数据帧解析后的包头信息，重复查表、匹配并执行对应的交换机指令的过程。</p>
<p>P4交换机中也有流水线（pipeline）的概念，一条流水线表示一组完整的数据处理流程，这一概念和传统交换机中的的流水线是相似的。如下图所示，在P4交换机中一条流水线可以包含以下组件：解析器&#x2F;逆解析器、匹配-动作表、元数据总线。其中除了元数据总线，其他组件都是非必须的。</p>
<p><img src="https://pic.downk.cc/item/5f0bffee14195aa5946bdb4d.png"></p>
<p><strong>解析器（parser）：</strong>将分组数据转化成元数据。<br><strong>逆解析器（Deparser）：</strong>将元数据转化成序列化的分组数据。<br><strong>匹配动作表（match-action table）：</strong>操作元数据。<br><strong>元数据（metadata）：</strong>在流水线内存储数据信息。</p>
<p>P4交换机中将流水线处理数据的过程进行抽象和重定义，数据处理单元对数据的处理抽象成匹配和执行匹配-动作表的过程，包头的解析抽象成P4中的解析器，数据处理流程抽象成流控制。P4中基础数据处理单元是不记录数据的，所以就需要引入一个元数据总线，用来存储一条流水线处理过程中需要记录的数据。P4交换机的专用物理芯片Tofino，最高支持12个数据处理单元，可以覆盖传统交换机的所有功能。</p>
<p>有了以上的知识储备，就可以很轻松的刻画出P4交换机的结构。如下图所示，P4交换机中含有两条流水线——入口流水线和出口流水线；同时还有一些数据流管理功能，例如：拥塞控制，队列控制，流量复制等。</p>
<p><img src="https://pic.downk.cc/item/5f0c016b14195aa5946c4395.png"></p>
<p>我们不难看出P4交换机结构与传统的交换机并没有多少差别，而且在芯片的设计上也仅仅是增加了可以自定义基础数据处理单元和转发流程的功能，其他设计与传统交换机芯片无异，这也使得P4交换机能够在为用户提供数据转发平面的可编程能力的同时，保证数据的线性转发速率。</p>
<h4 id="P4程序工作流程"><a href="#P4程序工作流程" class="headerlink" title="P4程序工作流程"></a>P4程序工作流程</h4><p>我们可以梳理出P4的完整工作流程。用户首先需要自定义数据帧的解析器和流控制程序，其次P4程序经过编译器编译后输出JSON格式的交换机配置文件和运行时的API，再次配置的文件载入到交换器中后更新解析起和匹配－动作表，最后交换机操作系统按照流控制程序进行包的查表操作。</p>
<p><img src="https://pic.downk.cc/item/5f0c064d14195aa5946dafc4.png"></p>
<p>如上图所示，以新增VLAN包解析为例，图中解析器除VXLAN以外的包解析是交换机中已有的，载入VXLAN.p4文件所得的配置文件的过程就是交换机的重配置过程。配置文件载入交换机后，解析器中会新增对VXLAN包解析，同时更新匹配-动作表，匹配成功后执行的动作也是在用户自定的程序中指定。执行动作需要交换机系统调用执行动作对应的指令来完成，这时交换机系统调用的是经过P4编译器生成的统一的运行时API，这个API就是交换机系统调用芯片功能的驱动，流控制程序就是指定API对应的交换机指令。</p>
<h4 id="P4源码目录结构"><a href="#P4源码目录结构" class="headerlink" title="P4源码目录结构"></a>P4源码目录结构</h4><p>P4项目源码可以在<a target="_blank" rel="noopener" href="https://github.com/p4lang">github</a>上直接获取。P4项目由很多个单独的模块组成，每个模块就是一个子项目，下面分别简单介绍一下各模块的功能。</p>
<p><strong>（1）behavioral-model</strong><br>模拟P4数据平面的用户态软件交换机，使用C++语言编写，简称bmv2。P4程序首先经过p4c-bm模块编译成JSON格式的配置文件，然后将配置文件载入到bmv2，转化成能实现交换机功能的数据结构。</p>
<p>behavioral-model模块是架构无关的，可以实现各种P4编程目标。该模块主要实现三个目标，其中最重要的是simple_switch,即实现P4语言标准中抽象交换机模型。另外两个目标是（simple_router,l2_switch），这两个目标是作为教学示例。</p>
<p><strong>（2）p4-hlir</strong><br>将P4代码转换成高级中间表示的前端编译器，目前的高级中间表示的展示形式与python对象的层次结构相同。该编译器的目的是使得后端编译器开发者从语法分析和目标无关的语义检查的负担中解放出来。</p>
<p><strong>（3）p4c-bm</strong><br>behavioral modal的后端编译器，建立在p4-hilr的顶部，该模块以P4程序作为输入，输出一个可以载入到behavioral model的JSON配置文件。</p>
<p><strong>（4）p4-build</strong><br>需要手动生成的基础设施库，为执行P4程序编译、安装PD库。</p>
<p><strong>（5）switch</strong><br>内含switch.p4程序样例以及通过SAI、SwitchAPI和Switchlink操作交换机所需的所有库，可独立于p4factory运行 。</p>
<p><strong>（6）ntf（Network Test Framework）</strong><br>网络测试框架，内含用以执行bmv2上应用的网络测试样例。该框架中集成了mininet和docker，方便用户进行测试。</p>
<p><strong>（7）p4factory</strong><br>内含整套用以运行和开发基于behavioral model的P4程序环境的代码，帮助用户快速开发P4程序。</p>
<p><strong>（8）ptf</strong><br>数据平面测试框架，基于unittest框架实现，内含标准Python版本。该框架中的大部分代码从floodlight项目中的OFTest框架移植而来，框架的实现和开发可参考OFTest框架文档。</p>
<p><strong>（9）scapy-vxlan</strong><br>基于Scapy项目，barefoot对其进行了定制，支持更多协议的数据包包头的伪造和解析，目前支持 VXLAN和ERSPAN-like（Scapy本身并不支持）。</p>
<p><strong>（10）tutorials</strong><br>P4语言教程，内含8个教程,覆盖了P4语言中的解析器、动作、状态存储、匹配-动作表、等基础组件。<br>1）cpoy_to_cpu：基本动作clone_ingress_to_egress教程<br>2）meter：计量表教程<br>3）TLV_parsing：IPv4数据包解析教程<br>4）register：寄存器读写状态教程<br>5）counter：计数器教程<br>6）action_profile：ECMP动作摘要教程<br>7）resubmit：数据包冲提交到入端口流水线教程<br>8）simple_nat：TCP流量的完全圆锥形NAT网络教程<br>注：P4语言项目库中的SAI、mininet及thrift是从其他开源项目完全fork而来，这里不展开讨论。</p>
<h4 id="基础数据类型"><a href="#基础数据类型" class="headerlink" title="基础数据类型"></a>基础数据类型</h4><h5 id="布尔型（bool）"><a href="#布尔型（bool）" class="headerlink" title="布尔型（bool）"></a>布尔型（bool）</h5><table>
<thead>
<tr>
<th align="center">运算符</th>
<th align="center">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="center">and</td>
<td align="center">双目运算符，结果为布尔型</td>
</tr>
<tr>
<td align="center">or</td>
<td align="center">双目运算符，结果为布尔型</td>
</tr>
<tr>
<td align="center">not</td>
<td align="center">单目运算符，结果为布尔型</td>
</tr>
<tr>
<td align="center">&#x3D;&#x3D;，!&#x3D;</td>
<td align="center">相等或不等，结果为布尔型</td>
</tr>
</tbody></table>
<h5 id="无符号整型（bit）"><a href="#无符号整型（bit）" class="headerlink" title="无符号整型（bit）"></a>无符号整型（bit）</h5><p>又称之为位串（bit-string），对位串进行数学运算的时候，位串长度必须是8的整数倍。</p>
<table>
<thead>
<tr>
<th align="center">运算符</th>
<th align="center">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="center">＝＝，！＝</td>
<td align="center">是否相等或不等，运算结果为布尔型。</td>
</tr>
<tr>
<td align="center">&lt;，&gt;，&lt;&#x3D;，&gt;&#x3D;</td>
<td align="center">无符号数比较，操作数的长度（W）要求相同，运算结果为布尔型。</td>
</tr>
<tr>
<td align="center">&amp;，｜，^</td>
<td align="center">按位运算符（分别是与，或和异或），操作数的长度（W）要求相同，运算结果为无符号整型。</td>
</tr>
<tr>
<td align="center">～</td>
<td align="center">运算结果为操作数的补码。</td>
</tr>
<tr>
<td align="center">&lt;&lt;，&gt;&gt;</td>
<td align="center">左移运算符操作数为无符号整型，右移运算符操作数必须是无符号数或非负整数。此运算符为逻辑位移。</td>
</tr>
<tr>
<td align="center">+（单目）</td>
<td align="center">单目加运算，效果同no-op。（无意义）</td>
</tr>
<tr>
<td align="center">-（单目）</td>
<td align="center">单目减运算，计算结果为2W减去操作数，W为操作数长度。</td>
</tr>
<tr>
<td align="center">+（双目）</td>
<td align="center">二目加运算，操作数的长度（W）要求相同。计算结果为操作数的算术和，且运算结果长度也必须为W，超过则截断。</td>
</tr>
<tr>
<td align="center">-（双目）</td>
<td align="center">二目减运算，操作数的长度（W）要求相同。计算结果为操作数的算术差。</td>
</tr>
<tr>
<td align="center">*</td>
<td align="center">无符号乘法运算，操作数的长度（W）要求相同，计算结果为无符号数且长度与操作数相等。</td>
</tr>
</tbody></table>
<h5 id="有符号整数型（int（W））"><a href="#有符号整数型（int（W））" class="headerlink" title="有符号整数型（int（W））"></a>有符号整数型（int（W））</h5><p>大致与无符号整数相同</p>
<table>
<thead>
<tr>
<th align="center">运算符</th>
<th align="center">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="center">-（单目）</td>
<td align="center">单目减运算，运算结果伟有符号整型，长度和操作数相等</td>
</tr>
<tr>
<td align="center">+（双目）</td>
<td align="center">二目加运算，操作数数据类型必须相同，运算结果也为同类型。</td>
</tr>
<tr>
<td align="center">-（双目）</td>
<td align="center">二目减运算，操作数数据类型必须相同，运算结果也为同类型。</td>
</tr>
<tr>
<td align="center">*</td>
<td align="center">有符号乘法运算，操作数的长度（W）要求相同，计算结果为有符号数且长度与操作数相等。</td>
</tr>
</tbody></table>
<h5 id="变长位串（varbit）"><a href="#变长位串（varbit）" class="headerlink" title="变长位串（varbit）"></a>变长位串（varbit）</h5><p>该数据类型，不支持算术、比较和位运算，也不支持类型转换，该数据类型在定义时会指定一个静态的最大宽度值，解析器会提取变长位串数据并设置一个值作为长度。</p>
<h5 id="无限精度整型（int）"><a href="#无限精度整型（int）" class="headerlink" title="无限精度整型（int）"></a>无限精度整型（int）</h5><table>
<thead>
<tr>
<th align="center">运算符</th>
<th align="center">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="center">＝＝，！＝</td>
<td align="center">是否相等或不等，运算结果为布尔型。</td>
</tr>
<tr>
<td align="center">&lt;，&gt;，&lt;&#x3D;，&gt;&#x3D;</td>
<td align="center">有符号数比较，运算结果为布尔型。</td>
</tr>
<tr>
<td align="center">&lt;&lt;，&gt;&gt;</td>
<td align="center">右移运算符操作数必须为正整数；左移运算结果和操作数相同。a&lt;&lt;b等价于ax2b，a&gt;&gt;b等价于a&#x2F;2b。</td>
</tr>
<tr>
<td align="center">+（单目）</td>
<td align="center">单目加运算，效果同no-op。（无意义）</td>
</tr>
<tr>
<td align="center">-（单目）</td>
<td align="center">单目减运算，运算结果为整型，且该运算不会导致溢出。</td>
</tr>
<tr>
<td align="center">+（双目）</td>
<td align="center">二目加运算，操作数类型都必须是整型，运算结果为整型，且该运算不会导致溢出。</td>
</tr>
<tr>
<td align="center">-（双目）</td>
<td align="center">二目减运算，操作数类型都必须是整型，计算结果为整型，且该运算不会导致溢出。</td>
</tr>
<tr>
<td align="center">*</td>
<td align="center"><strong>无符号乘法运算</strong>，操作数必须都是整形，计算结果为整形，该运算不会导致溢出。</td>
</tr>
<tr>
<td align="center">／，％</td>
<td align="center">二目有符号除法和取模运算，操作数必须是正整数，运算结果为正整数。</td>
</tr>
</tbody></table>
<h4 id="数据类型转换"><a href="#数据类型转换" class="headerlink" title="数据类型转换"></a>数据类型转换</h4><p>可以看出，上述的很多运算都是建立在相同数据类型的前提下，但是对于某些数据类型是不能相互转换的，p4也提供了合法的数据类型转换。</p>
<table>
<thead>
<tr>
<th align="center">from</th>
<th align="center">to</th>
<th align="center">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="center">bit&lt;1&gt;</td>
<td align="center">bool</td>
<td align="center">0代表false，1代表true（中括号里的1代表的是字节数，不是0和1，下同）</td>
</tr>
<tr>
<td align="center">bool</td>
<td align="center">bit&lt;1&gt;</td>
<td align="center">同上</td>
</tr>
<tr>
<td align="center">bit<w></td>
<td align="center">int<w></td>
<td align="center">保留所有比特位不变</td>
</tr>
<tr>
<td align="center">int<w></td>
<td align="center">bit<w></td>
<td align="center">同上</td>
</tr>
<tr>
<td align="center">bit<w></td>
<td align="center">bit<w></td>
<td align="center">当w&gt;w1时，保留低位w1位长度的数据，小于时新增位补0</td>
</tr>
<tr>
<td align="center">int<w></td>
<td align="center">int<w></td>
<td align="center">当W&gt;W1时，保留低位W1长度的数字，当W&lt;W1时新增位补符号位.(补符号位？)</td>
</tr>
<tr>
<td align="center">int</td>
<td align="center">bit<w></td>
<td align="center">保留低位W位长度数据，溢出需要发出警告并转化为负数。</td>
</tr>
<tr>
<td align="center">int</td>
<td align="center">intt<w></td>
<td align="center">保留低位W位长度数据，溢出需要发出警告。</td>
</tr>
</tbody></table>
<p>这些属于显式类型转换，p4中同样存在隐式类型转换，在运算中会进行强制转换，这个和大部分语言类似。也容易发生溢出错误。</p>
<h4 id="基础语言组件"><a href="#基础语言组件" class="headerlink" title="基础语言组件"></a>基础语言组件</h4><p>之前有学到过，p4程序主要由五个组件构成：header，parser，table，action，和controller</p>
<h5 id="首部header"><a href="#首部header" class="headerlink" title="首部header"></a>首部header</h5><p>这个类似于固定属性一样的东西，有多个成员字段，Header分为两种，一种是包头（Packet Header），一种是元数据（Metadata）。</p>
<p>首部类型是由成员字段组成的有序列表，每个字段都有其名称和长度，每一种首部类型都有对应的首部实例来存储具体的数据。首部分为两种，一种是包头（Packet Headers），另一种是元数据（Metadata）。</p>
<ul>
<li><p>包头</p>
<p>包头用以描述数据包结构，以IPv4协议为例，图1为 IPv4报文头部结构，IPv4报头有20字节固定长度部分和可选字段、填充字段的可变部分，每个字段的作用这里不再赘述。</p>
<p>ipv4包头</p>
<p><img src="https://pic.downk.cc/item/5f0c1cc814195aa59474a057.png"></p>
<p>P4包头</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">header <span class="type">ipv4_t</span>&#123;</span><br><span class="line">    fields&#123;</span><br><span class="line">        version:<span class="number">4</span>;</span><br><span class="line">        ihl:<span class="number">4</span>;</span><br><span class="line">        diffserv:<span class="number">8</span>;</span><br><span class="line">        totallen:<span class="number">16</span>;</span><br><span class="line">        identification:<span class="number">16</span>;</span><br><span class="line">        flags:<span class="number">3</span>;</span><br><span class="line">        fragoffset:<span class="number">13</span>;</span><br><span class="line">        ttl:<span class="number">8</span>;</span><br><span class="line">        protocol:<span class="number">8</span>;</span><br><span class="line">        hdrchecksum:<span class="number">16</span>;</span><br><span class="line">        srcaddr:<span class="number">32</span>;</span><br><span class="line">        dstaddr:<span class="number">32</span>;</span><br><span class="line">        option:*;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"># 里面的数字都是以bit为单位的，所以其实应该也能写成这样。</span><br><span class="line">header <span class="type">ipv4_t</span>&#123;</span><br><span class="line">    field&#123;</span><br><span class="line">        bit&lt;<span class="number">4</span>&gt; version;</span><br><span class="line">        bit&lt;<span class="number">4</span>&gt; ihl;</span><br><span class="line">        bit&lt;<span class="number">8</span>&gt; diffserv;</span><br><span class="line">        bit&lt;<span class="number">16</span>&gt; totallen;</span><br><span class="line">        bit&lt;<span class="number">16</span>&gt; identification;</span><br><span class="line">        bit&lt;<span class="number">3</span>&gt; flags;</span><br><span class="line">        bit&lt;<span class="number">13</span>&gt; fragoffset;</span><br><span class="line">        bit&lt;<span class="number">8</span>&gt; ttl;</span><br><span class="line">        bit&lt;<span class="number">8</span>&gt; protocol;</span><br><span class="line">        bit&lt;<span class="number">16</span>&gt; hdrchecksum;</span><br><span class="line">        bit&lt;<span class="number">32</span>&gt; srcaddr;</span><br><span class="line">        bit&lt;<span class="number">32</span>&gt; dstaddr;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>ipv6</p>
<p><img src="https://pic.downk.cc/item/5f0c202c14195aa59475cb4b.png"></p>
<p>p4</p>
<figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">header ipv6_t&#123;</span><br><span class="line">    field&#123;</span><br><span class="line">        bit&lt;<span class="number">4</span>&gt; version;</span><br><span class="line">        bit&lt;<span class="number">8</span>&gt; trafficclass;</span><br><span class="line">        bit&lt;<span class="number">20</span>&gt; flowlabel;</span><br><span class="line">        bit&lt;<span class="number">20</span>&gt; payloadlen;</span><br><span class="line">        bit&lt;<span class="number">8</span>&gt; nxthdr;</span><br><span class="line">        bit&lt;<span class="number">8</span>&gt; hoplimit;</span><br><span class="line">        bit&lt;<span class="number">128</span>&gt; srcaddr;</span><br><span class="line">        bit&lt;<span class="number">128</span>&gt; dstaddr;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><h4 id="元数据"><a href="#元数据" class="headerlink" title="元数据"></a>元数据</h4><p>元数据用来携带数据和配置信息，元数据的申明与包头类似，但在实例化时不同，而且包头和元数据在字段值的约束上存在一定的差别。元数据分为两种，一种是用来携带P4程序运行过程中产生的数据的用户自定义元数据（User-Defined Metadata），如首部字段的运算结果等。另一种是固有元数据（Intrinsic Metadata），用于携带交换机自身的配置信息，如数据包进入交换机时的端口号等。</p>
<figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> ingress_metadata_t&#123;</span><br><span class="line">    ipv4_t ipv4</span><br><span class="line">    ipv6_t ipv6</span><br><span class="line">&#125;</span><br><span class="line">metadate ingress_metadata_t ingress_metadata;</span><br></pre></td></tr></table></figure>

<p>用户可以使用自定义的元数据来携带任意数据，但固有元数据在编译器中具有特定的意义。V1.1中定义了8种固有元数据，这些元数据携带了数据包相关的状态信息，表中展示8种标准固有元数据及其作用。</p>
<table>
<thead>
<tr>
<th align="center">字段</th>
<th align="center">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="center">ingress_port</td>
<td align="center">数据包的入端口，解析之前设置。只读。</td>
</tr>
<tr>
<td align="center">packet_length</td>
<td align="center">数据包的字节数，当交换机在快速转发模式下，该元数据不能在动作（action）中匹配或引用。只读。</td>
</tr>
<tr>
<td align="center">egress_spec</td>
<td align="center">在入端口流水线的匹配-动作过程之后设置，指定数据包出端口，可以是物理端口、逻辑端口或者多播组。</td>
</tr>
<tr>
<td align="center">egress_port</td>
<td align="center">指定数据包的物理出端口，区别于egress_spec，只能应用于物理端口。只读。</td>
</tr>
<tr>
<td align="center">egress_instance</td>
<td align="center">用于区分复制后数据包实例的标识符。只读。</td>
</tr>
<tr>
<td align="center">instance_type</td>
<td align="center">数据包实例类型：正常（Normal）、入端口复制（ingress clone）、出端口复制（egress clone）、再循环（recirculated）。</td>
</tr>
<tr>
<td align="center">parser_status</td>
<td align="center">解析器解析结果，0表示无错误，其实数字代表了对应的错误类型。</td>
</tr>
<tr>
<td align="center">parser_error_loaction</td>
<td align="center">指向P4程序错误发生处。</td>
</tr>
</tbody></table>
<p>在P4语言中定义首部类型有以下几点需要注意：<br>1)包头类型的长度需要字节对齐，即长度必须是8bit的整数倍。<br>2)包头中字段长度可以是可变值（该特性在P4语言规范中规定，但当前编译器版本并为实现，后续版本会支持）也可以是首部中其他字段值计算后的值。而元数据中的字段长度只能是定值。<br>3)只有包头能够实例化成数组，元数据则不行。<br>4)实例化时，首部中已定义名称的字段的值会被初始化成程序中的指定值，如果首部中只定义字段名称而未指定值，字段的值将会被初始化成0。</p>
</li>
</ul>
<h5 id="解析器parser"><a href="#解析器parser" class="headerlink" title="解析器parser"></a>解析器parser</h5><p>一个P4程序中往往定义了大量的首部和首部实例，但并不是所有的首部实例都会对数据包进行操作。解析器工作时会生成描述数据包进行哪些匹配+动作操作的中间表示（ Intermediate Representation），在P4中称之为解析后表示（Parsed Representation），这些解析后表示规定了对数据包生效的实例，是一组对数据包生效的实例的集合。</p>
<p>P4语言中解析器采用有限状态机的设计思路，每个解析器方法视为一种状态。当解析器工作时，会将当前处理的数据包头字节的偏移量记录在首部实例中，并在状态迁移（调用另一个解析器）时指向包头中下一个待处理的有效字节。以以太网帧的解析器为例，用数据包类型代对应解析器，将每个解析器作为一种状态，用箭头表示状态迁移，则可以构建出如图2所示的以太网帧的解析器的状态迁移图。</p>
<p><img src="https://pic.downk.cc/item/5f0d625414195aa594d0a989.png"></p>
<p>展示了以太网帧和IPv4数据包解析器定义示例</p>
<p><img src="https://pic.downk.cc/item/5f0d8f8314195aa594dd942d.png"></p>
<p>一个解析方法&#x2F;状态可以以下四种方式结束：<br>1）return 一个流控制程序名<br>2）return一个解析器名<br>3）发生显式错误<br>4）发生隐式错误<br>P4语言中流控制程序和解析器的命名空间是共用的，所以在定义解析器和流控制程序的时候需要注意不能重名，否则会导致P4程序错误。</p>
<h5 id="动作action"><a href="#动作action" class="headerlink" title="动作action"></a><strong>动作</strong>action</h5><p>P4语言中的动作主要分为两种，基本动作（Primitive Actions）和复合动作（Compound Actions）。基本动作包括：数据包处理运算符（如添加、删除或修改包头）、基本的算术运算符、哈希运算符和统计跟踪运算符（如计量、测量）。复合动作由基本动作组合而成，由用户自行定义。表中展示了P4中定义的基本动作。</p>
<table>
<thead>
<tr>
<th align="center">动作</th>
<th align="center">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="center">no_op</td>
<td align="center">占位符动作，不做任何操作。</td>
</tr>
<tr>
<td align="center">drop</td>
<td align="center">在入口流水线中将数据包丢弃。</td>
</tr>
<tr>
<td align="center">modify_field</td>
<td align="center">修改解析后表示中的包头字段值。</td>
</tr>
<tr>
<td align="center">modify_field_with_hash_based_index</td>
<td align="center">使用字段列表索引计算一个值并使用该值生成偏移量。</td>
</tr>
<tr>
<td align="center">add_header</td>
<td align="center">为数据包的解析后表示添加包头。</td>
</tr>
<tr>
<td align="center">remove_header</td>
<td align="center">为数据包的解析后表示删除包头。</td>
</tr>
<tr>
<td align="center">copy_header</td>
<td align="center">复制首部实例。</td>
</tr>
<tr>
<td align="center">push</td>
<td align="center">将所有首部实例压入一个数组，并在顶部添加一个新首部。</td>
</tr>
<tr>
<td align="center">pop</td>
<td align="center">将实例数组顶部的元素弹出，后续元素向顶部移位。</td>
</tr>
<tr>
<td align="center">count</td>
<td align="center">更新计数器。</td>
</tr>
<tr>
<td align="center">meter</td>
<td align="center">执行计量操作。</td>
</tr>
<tr>
<td align="center">generate_digest</td>
<td align="center">生成一个报文摘要并发送到接收机。</td>
</tr>
<tr>
<td align="center">truncate</td>
<td align="center">在出口处截断数据包。</td>
</tr>
<tr>
<td align="center">resubmit</td>
<td align="center">将原始数据包和元数据重新发送到解析器。</td>
</tr>
<tr>
<td align="center">Recirculate</td>
<td align="center">在数据包完成出口修改操作后重新发送。</td>
</tr>
<tr>
<td align="center">clone_ingress_pkt_to_ingress</td>
<td align="center">复制原始数据包并发送到解析器。</td>
</tr>
<tr>
<td align="center">clone_egress_pkt_to_ingress</td>
<td align="center">复制出口数据包并发送到解析器。</td>
</tr>
<tr>
<td align="center">clone_ingress_pkt_to_egress</td>
<td align="center">复制原始数据包并发送到缓存区。</td>
</tr>
<tr>
<td align="center">clone_egress_pkt_to_egress</td>
<td align="center">复制出口数据包并发送的缓存区。</td>
</tr>
</tbody></table>
<p>这些动作高度抽象且与协议无关，以实现P4语言处理数据的协议无关性。同时，复杂的操作及流程可以通过组合不同基本操作（即复合操作）完成，从而保障了P4语言对各种协议的支持以及扩展性。下图展示了P4中复合动作定义的示例。</p>
<p><img src="https://pic.downk.cc/item/5f0d954514195aa594df0cbc.png"></p>
<h5 id="匹配-动作表Tables"><a href="#匹配-动作表Tables" class="headerlink" title="匹配-动作表Tables"></a><strong>匹配-动作表</strong>Tables</h5><p>P4语言中的匹配-动作表定义了匹配字段、动作及一些相关属性（如表容量），当匹配-动作表中定义的字段与数据包匹配成功时，则执行对应的动作；若匹配不成功则标记为“失配（miss）”，并执行默认操作。匹配动作表的定义如图所示。</p>
<p><img src="https://pic.downk.cc/item/5f0d964a14195aa594df4fb2.png"></p>
<p>P4语言的匹配-动作表支持多种匹配类型，如精确匹配、最长前缀匹配、范围匹配等。如表所示，展示了动作-匹配表支持的匹配类型。</p>
<table>
<thead>
<tr>
<th align="center">匹配类型</th>
<th align="center">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="center">excat</td>
<td align="center">精确匹配。</td>
</tr>
<tr>
<td align="center">ternary</td>
<td align="center">三重匹配，动作-匹配表的每个表项都有一个掩码，将掩码和字段值进行逻辑与运算，再执行匹配。为了避免导致多条表项匹配成功，每条表项都需要设定一个优先级。</td>
</tr>
<tr>
<td align="center">lpm</td>
<td align="center">这是三重匹配的一种特殊情况，当多个表项匹配成功时，选择掩码最长的最为最高优先级进行匹配。</td>
</tr>
<tr>
<td align="center">index</td>
<td align="center">字段值作为表项索引。</td>
</tr>
<tr>
<td align="center">range</td>
<td align="center">表项中确定一个范围，字段值在此范围内皆能成功匹配。</td>
</tr>
<tr>
<td align="center">valid</td>
<td align="center">仅用于包头字段匹配，表项值只能为true&#x2F;false。</td>
</tr>
</tbody></table>
<h5 id="流控制程序control"><a href="#流控制程序control" class="headerlink" title="流控制程序control"></a><strong>流控制程序</strong>control</h5><p>P4语言中匹配-动作表中规定需要匹配的字段和需要执行的操作，流控制程序则用来规定匹配-动作表的执行顺序。</p>
<p>以P4语言定义二层转发流程为例，数据包首先进行L2转发表（l2_fwd）匹配，然后根据数据包的以太网目的地址是否匹配路由器自身的MAC地址（通过查找所属的router_mac表）决定是否经过l3路由表（ipv4_fib_lpm和upv6_fib_lpm），再根据IP包头类型（IPv4或IPv6），数据包匹配不同的L3路由表，最后通过访问控制列表来控制数据包是否通过。</p>
<p><img src="https://pic.downk.cc/item/5f0d9e2114195aa594e1989f.png"></p>
<h4 id="状态存储"><a href="#状态存储" class="headerlink" title="状态存储"></a>状态存储</h4><p>包头和元数据实例中的数据只能存在对某个数据包解析的过程中，解析下一个数据包时，这些实例会重新初始化。而计数器、计量器和寄存器中的数据在整个流水线中长期存在，所以称之为状态存储。</p>
<p><strong>计数器</strong></p>
<p>计数器附加在每个表项之后，并在完成一次匹配并执行对应操作后自增1。计数器中定义了7种属性，下图展示了V1.1中计数器的定义方式。</p>
<p><img src="https://pic.downk.cc/item/5f0d9f7d14195aa594e2036b.png"></p>
<p>1）Name<br>计数器名称，指向该计数器，P4编译器中通过名称+索引的方式确定一个计数器实例。</p>
<p>2）min_width<br>编译P4程序时，编译器分配给计数器的大小并不是完全固定的，该属性指定了分配给计数器的最小长度。</p>
<p>3）saturating<br>如果计数器中设定了该属性，则当计数器到达上限时停止计数，否则计数器将清零并重新开始计数。</p>
<p>4）direct<br>如果计数器中设定了该属性，则计数器绑定的匹配-动作表中无需指定count动作来更新计数器，计数器会自动更新。若在匹配动作表调用count动作更新计数器，则编译器报错。</p>
<p>5）static<br>如果计数器中设定了该属性，则必须在匹配-动作表中调用count动作更新计数器。</p>
<p>6）instance_count<br>该属性用以记录计数器实例数，如果计数器设定了direct属性，则无法在计数器中设定该属性；如果计数器中未设定direct属性，则该属性必须设定。</p>
<p>7）type<br>V1.1中的计数器类型有3种： bytes、packets、bytes_and_packets。</p>
<h5 id="计量器"><a href="#计量器" class="headerlink" title="计量器"></a><strong>计量器</strong></h5><p><img src="https://pic.downk.cc/item/5f0d9fed14195aa594e2202d.png"></p>
<p>1）name<br>计量器名称，指向该计量器。</p>
<p>2）direct<br>如果计量器中设定了该属性，则计量器绑定的匹配-动作表中无需指定execute_meter动作来更新计量器，计数器会自动更新。若在匹配动作表调用execute_meter动作更新计量器，则编译器报错。</p>
<p>3）static<br>如果计数器中设定了该属性，则必须在匹配-动作表中调用execute_meter动作更新计数器。</p>
<p>4）instance_count<br>该属性用以记录计量器实例数，如果计量器设定了direct属性，则无法在计量器中设定该属性；如果计量器中未设定direct属性，则该属性必须设定。</p>
<p>5）type<br>V1.1中的计量器类型有2种： bytes、packets。</p>
<p>6）result<br>V1.1中的计量器的输出结果有3种，分别用三种颜色标记：红色（P4_METER_COLOR_RED）、黄色（P4_METER_COLOR_YELLO）和绿色（P4_METER_COLOR_GREEN），输出结果存在一个2bit长度的字段中。</p>
<h5 id="寄存器"><a href="#寄存器" class="headerlink" title="寄存器"></a><strong>寄存器</strong></h5><p><img src="https://pic.downk.cc/item/5f0da00e14195aa594e22a71.png"></p>
<p>1）name<br>寄存器名称，指向该寄存器，P4编译器中通过名称+索引的方式确定一个计量器实例。</p>
<p>2）width_or_layout<br>width和layout属性二选一，width为指定一个确定的长度，而 layout是直接通过名称引用已定义的包头结构。</p>
<p>3）direct_or_static<br>与计数器和计量器中的定义类似，虽然寄存器不能直接在匹配过程中使用，但是作为modify_field动作的数据源，将当前寄存器中的数据复制到数据包的元数据中，并在后续的匹配中使用。</p>
<p>4）instance_count<br>该属性用以记录寄存器实例数，如果寄存器设定了direct属性，则无法在寄存器中设定该属性；如果寄存器中未设定direct属性，则该属性必须设定。</p>
<h1 id="SDN控制平面"><a href="#SDN控制平面" class="headerlink" title="SDN控制平面"></a>SDN控制平面</h1><h3 id="OpenDayLight"><a href="#OpenDayLight" class="headerlink" title="OpenDayLight"></a>OpenDayLight</h3><h3 id="ONOS"><a href="#ONOS" class="headerlink" title="ONOS"></a>ONOS</h3><h1 id="SDN数据平面"><a href="#SDN数据平面" class="headerlink" title="SDN数据平面"></a>SDN数据平面</h1><h2 id="通用可编程转发模型"><a href="#通用可编程转发模型" class="headerlink" title="通用可编程转发模型"></a>通用可编程转发模型</h2><p>主要包括通用硬件模型和通用处理指令。通用硬件模型包括一系列通用硬件子模型，通用处理指令包括用户可编程的网络处理操作和控制指令</p>
<h3 id="通用硬件模型"><a href="#通用硬件模型" class="headerlink" title="通用硬件模型"></a>通用硬件模型</h3><p>网络处理流程：首先数据包从端口进入，通过协议解析模块进行头部解析，根据解析结果选择对应的流表进行处理，在选择的流表中数据包的内容与流表项进行一一匹配，匹配到的执行对应的action，没有匹配到的再按照特定指令进行处理，比如丢弃或者转发给控制器。</p>
<h4 id="流表抽象子模型"><a href="#流表抽象子模型" class="headerlink" title="流表抽象子模型"></a>流表抽象子模型</h4><p>每个流表项对应一个流Flow，流的定义是对应于匹配域相同的同一个流表去处理的数据包。例如浏览网页和看视频的数据包，不同网络地址发来的数据包，从应用级、会话级、用户级等颗粒度管理网络策略</p>
<p>流表项的主要内容：</p>
<table>
<thead>
<tr>
<th align="center">匹配域</th>
<th align="center">Instructions</th>
<th align="center">计数器</th>
<th align="center">优先级</th>
<th align="center">cookie</th>
<th align="center">flags</th>
</tr>
</thead>
<tbody><tr>
<td align="center">包括输入端口、包头标示域（五元组&#x2F;vlan id）、metadata</td>
<td align="center">指定数据包的跳转操作，也包括操作指令集的操作</td>
<td align="center">统计数据流信息</td>
<td align="center">数值越高优先级越高</td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<p>OpenFlow Switch需要从数据包中提取协议字段组合，再加上上一个流表的metadata信息共同组建一个待匹配域。然后将待匹配域与当前流表的流表项进行比较。</p>
<h4 id="组表抽象子模型"><a href="#组表抽象子模型" class="headerlink" title="组表抽象子模型"></a>组表抽象子模型</h4><p>组表也是一种转发表，具有给某个端口指定某种操作的抽象能力。组表由多个组表项组成，用户通过编程来定义这组端口及要执行的操作。通过在流表项使用Group动作可以将Flow数据包指向某个组操作，从而执行组表中的动作集合。</p>
<p>组表的表项组成：</p>
<table>
<thead>
<tr>
<th align="center">Group Identifier</th>
<th align="center">Group Type</th>
<th align="center">Counters</th>
<th align="center">Action Buckets</th>
</tr>
</thead>
<tbody><tr>
<td align="center">id标示</td>
<td align="center">必须支持：all、indirect&#x2F;可选支持：select、fast failover</td>
<td align="center">计数</td>
<td align="center">操作顺序列表</td>
</tr>
</tbody></table>
<h4 id="Meter表抽象子模型"><a href="#Meter表抽象子模型" class="headerlink" title="Meter表抽象子模型"></a>Meter表抽象子模型</h4><p>可以用来测量和控制某个Flow的传输速率</p>
<h4 id="状态信息表抽象子模型"><a href="#状态信息表抽象子模型" class="headerlink" title="状态信息表抽象子模型"></a>状态信息表抽象子模型</h4><p>多个计数器组合构成了网络状态信息表，包括流表、流表项、端口、队列、Group、Group Bucket、Meter、Duration计数器等。Duration计数器统计流表项、端口、队列、Group、Meter的存活时间。</p>
<h4 id="端口子模型"><a href="#端口子模型" class="headerlink" title="端口子模型"></a>端口子模型</h4><p>只有这三种标准端口才能作为输出、输入端口，才能在流表中使用，才能具有端口计数器和端口状态、配置信息</p>
<p>物理端口：真实网络接口，与网络设备中的物理接口一一对应</p>
<p>逻辑端口：抽象的逻辑端口，用来描述链路聚合组、隧道、环回接口等逻辑</p>
<p>保留端口：预留的虚拟端口类型</p>
<p>保留端口类型：</p>
<table>
<thead>
<tr>
<th align="center">名称</th>
<th align="center">description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">All</td>
<td align="center">转发数据包到所有端口，只能作为输出端口，数据包会被复制</td>
</tr>
<tr>
<td align="center">Controller</td>
<td align="center">控制通道，可作为输入&#x2F;输出</td>
</tr>
<tr>
<td align="center">Table</td>
<td align="center">通用转发模型处理流水线的起始位置，表示将数据包交给流水线的第一个流表来处理</td>
</tr>
<tr>
<td align="center">IN_port</td>
<td align="center">描述数据包的输入端口，只能作为输出端口，将数据包发送到自己的输入端口</td>
</tr>
<tr>
<td align="center">Any</td>
<td align="center">没有制定端口类型时使用的端口类型，不能用做输入端口和输出端口</td>
</tr>
<tr>
<td align="center">Local</td>
<td align="center">描述通用转发模型本地的网络栈和管理栈，可用做输入和输出端口。使得远端设备可以通过OpenFlow网络与通用转发模型交互，使用其网络服务，而不是通过一个独立的控制网络。结合流表项可以实现带内控制器链接</td>
</tr>
<tr>
<td align="center">Normal</td>
<td align="center">描述传统的非OpenFlow转发处理流水线，用作输出端口</td>
</tr>
<tr>
<td align="center">Flood</td>
<td align="center">传统数据平面的泛洪操作，转发数据包到不包括输入端口和OFPPS_BLOCKED状态的其他所有端口，只能用做输出端口</td>
</tr>
</tbody></table>
<h3 id="通用处理指令"><a href="#通用处理指令" class="headerlink" title="通用处理指令"></a>通用处理指令</h3><h4 id="网络处理控制指令Instructions"><a href="#网络处理控制指令Instructions" class="headerlink" title="网络处理控制指令Instructions"></a>网络处理控制指令Instructions</h4><p>基于Flow的网络处理流程控制指令Instructions，这种指令可以控制数据包在通用硬件模型流水线上的处理流程。包括两类：第一类是对Flow数据包的操作指令集进行写入、应用或者删除，比如write-actions、Apply-actions。第二类指令是指定Flow数据包在多个表中的处理顺序的跳转指令，比如GOTO-Table。</p>
<table>
<thead>
<tr>
<th align="center">控制指令</th>
<th align="center">description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">apply-action</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">clear-action</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">write-action</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">write-metadata</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">goto-table</td>
<td align="center"></td>
</tr>
</tbody></table>
<p>每个流表项都可以存在多个控制指令，这些控制指令组合成一个对应的指令集合，实际需要按照上面列出的顺序执行控制指令</p>
<h4 id="操作指令"><a href="#操作指令" class="headerlink" title="操作指令"></a>操作指令</h4><p>基于Flow的网络处理操作指令Actions，这种指令完成对数据包的丢弃、转发、复制、修改等操作。每一个数据包在进入流水线时都被分配一个action set，用于保存处理数据的动作，其集合是一个Set，集合中只能存在一个同一类型动作，此外OpenFlow Swtich还定义了一个Action List，List集合可以允许重复的类型动作。</p>
<table>
<thead>
<tr>
<th align="center">操作指令</th>
<th align="center">Description</th>
</tr>
</thead>
<tbody><tr>
<td align="center">Output port no</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Group Group ID</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Drop</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Set-Queue queueid</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Meter meter id</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Push-tag&#x2F;Pop-tag</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Set-Field</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">Copy-Field</td>
<td align="center"></td>
</tr>
<tr>
<td align="center">change-TTL</td>
<td align="center"></td>
</tr>
</tbody></table>
<p>当数据包到达通用硬件模型时，OpenFlow Swtich模型会给数据包分配空的操作指令集，当经过流表时，通过流表的Instruction控制指令完成对操作指令集的添加、删除、更新等。</p>
<h4 id="专用指令"><a href="#专用指令" class="headerlink" title="专用指令"></a>专用指令</h4><p>Table-miss定义了在流表中匹配不成功的处理网络数据包的行为</p>
<h1 id="SDN实验工具"><a href="#SDN实验工具" class="headerlink" title="SDN实验工具"></a>SDN实验工具</h1><h3 id="Cbench"><a href="#Cbench" class="headerlink" title="Cbench"></a>Cbench</h3><p>测试控制器性能的工具，Cbench可以模拟一系列软件交换机，然后向控制器发送OFP_Packet_In消息，从而测试控制器处理Packet_in消息的吞吐量和时延</p>
<h3 id="OFTest"><a href="#OFTest" class="headerlink" title="OFTest"></a>OFTest</h3><p>测试OpenFlow协议完整性的工具</p>
<h3 id="wireshark"><a href="#wireshark" class="headerlink" title="wireshark"></a>wireshark</h3><p>网络数据包分析</p>
<h3 id="发包工具"><a href="#发包工具" class="headerlink" title="发包工具"></a>发包工具</h3><h4 id="Iperf"><a href="#Iperf" class="headerlink" title="Iperf"></a>Iperf</h4><p>是一个数据包生成工具，可以向网络中打入TCP&#x2F;SCTP、UDP等数据流量。</p>
<p>Iperf测试模型由服务端和客户端两种角色，测试时由客户端发起数据传输，将数据发向服务端，进而得到带宽数据。</p>
<p>Iperf -s启动服务端 Iperf -c启动客户端</p>
<h4 id="Scapy，不是Scrapy"><a href="#Scapy，不是Scrapy" class="headerlink" title="Scapy，不是Scrapy"></a>Scapy，不是Scrapy</h4><p>可以构建多种协议数据包</p>
<p>进入scapy</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scapy</span><br></pre></td></tr></table></figure>

<p>在交互CLI输入</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">eth=ETHER()</span><br><span class="line">ip=IP(dst=&quot;8.8.8.8&quot;)</span><br><span class="line">tcp=TCP(dport=8080)</span><br><span class="line">pay(&quot;i am Mr.Test&quot;)</span><br><span class="line">packet=eth/ip/tcp/pay</span><br><span class="line">send(packet)</span><br></pre></td></tr></table></figure>

<h1 id="P4相关"><a href="#P4相关" class="headerlink" title="P4相关"></a>P4相关</h1><h2 id="behavioral-model"><a href="#behavioral-model" class="headerlink" title="behavioral-model"></a>behavioral-model</h2><p>模拟P4数据平面的用户态软件交换机，使用C++语言编写，简称bmv2。P4程序首先经过p4c-bm模块编译成JSON格式的配置文件，然后将配置文件载入到bmv2，转化成能实现交换机功能的数据结构。</p>
<p>behavioral-model模块是架构无关的，可以实现各种P4编程目标。该模块主要实现三个目标，其中最重要的是simple_switch,即实现P4语言标准中抽象交换机模型。另外两个目标是（simple_router,l2_switch），这两个目标是作为教学示例。</p>
<h2 id="p4-hlir"><a href="#p4-hlir" class="headerlink" title="p4-hlir"></a>p4-hlir</h2><p>将P4代码转换成高级中间表示的前端编译器，目前的高级中间表示的展示形式与python对象的层次结构相同。该编译器的目的是使得后端编译器开发者从语法分析和目标无关的语义检查的负担中解放出来。</p>
<h2 id="p4c-bm"><a href="#p4c-bm" class="headerlink" title="p4c-bm"></a>p4c-bm</h2><p>behavioral modal的后端编译器，建立在p4-hilr的顶部，该模块以P4程序作为输入，输出一个可以载入到behavioral model的JSON配置文件。</p>
<h2 id="p4-build"><a href="#p4-build" class="headerlink" title="p4-build"></a>p4-build</h2><p>需要手动生成的基础设施库，为执行P4程序编译、安装PD库。</p>
<h2 id="switch"><a href="#switch" class="headerlink" title="switch"></a>switch</h2><p>内含switch.p4程序样例以及通过SAI、SwitchAPI和Switchlink操作交换机所需的所有库，可独立于p4factory运行 。</p>
<h2 id="ntf（Network-Test-Framework）"><a href="#ntf（Network-Test-Framework）" class="headerlink" title="ntf（Network Test Framework）"></a>ntf（Network Test Framework）</h2><p>网络测试框架，内含用以执行bmv2上应用的网络测试样例。该框架中集成了mininet和docker，方便用户进行测试。</p>
<h2 id="p4factory"><a href="#p4factory" class="headerlink" title="p4factory"></a>p4factory</h2><p>内含整套用以运行和开发基于behavioral model的P4程序环境的代码，帮助用户快速开发P4程序。</p>
<h2 id="ptf"><a href="#ptf" class="headerlink" title="ptf"></a>ptf</h2><p>数据平面测试框架，基于unittest框架实现，内含标准Python版本。该框架中的大部分代码从floodlight项目中的OFTest框架移植而来，框架的实现和开发可参考OFTest框架文档。</p>
<h2 id="scapy-vxlan"><a href="#scapy-vxlan" class="headerlink" title="scapy-vxlan"></a>scapy-vxlan</h2><p>基于Scapy项目，barefoot对其进行了定制，支持更多协议的数据包包头的伪造和解析，目前支持 VXLAN和ERSPAN-like（Scapy本身并不支持）。</p>
<h2 id="tutorials"><a href="#tutorials" class="headerlink" title="tutorials"></a>tutorials</h2><p>P4语言教程，内含8个教程,覆盖了P4语言中的解析器、动作、状态存储、匹配-动作表、等基础组件。<br>1）cpoy_to_cpu：基本动作clone_ingress_to_egress教程<br>2）meter：计量表教程<br>3）TLV_parsing：IPv4数据包解析教程<br>4）register：寄存器读写状态教程<br>5）counter：计数器教程<br>6）action_profile：ECMP动作摘要教程<br>7）resubmit：数据包冲提交到入端口流水线教程<br>8）simple_nat：TCP流量的完全圆锥形NAT网络教程<br>注：P4语言项目库中的SAI、mininet及thrift是从其他开源项目完全fork而来，这里不展开讨论。</p>
<h2 id="Header"><a href="#Header" class="headerlink" title="Header"></a>Header</h2><p>Header代表了哪些格式的Header可以使用，例如Ethernet header。</p>
<p>例如Ethernet定义：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">header_type ethernet_t<span class="punctuation">&#123;</span></span><br><span class="line">	fields<span class="punctuation">&#123;</span></span><br><span class="line">  	dst_addr <span class="punctuation">:</span> <span class="number">48</span>;</span><br><span class="line">  	src_addr <span class="punctuation">:</span> <span class="number">48</span>;</span><br><span class="line">  	ether_type <span class="punctuation">:</span> <span class="number">16</span>;</span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>里面数字都是以bit定义的。</p>
<p>使用需要实例化：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">header ethernet_t ethernet;</span><br></pre></td></tr></table></figure>

<p>P4-16</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">header EthernetHeader &#123;</span><br><span class="line"></span><br><span class="line">    bit&lt;48&gt; dst;</span><br><span class="line">    bit&lt;48&gt; src;</span><br><span class="line">    bit&lt;16&gt; etherType;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">EthernetHeader ethernetHeader;</span><br></pre></td></tr></table></figure>



<h2 id="Parser"><a href="#Parser" class="headerlink" title="Parser"></a>Parser</h2><p>Header之后就要定义Parser，一般都有一个开始的parser：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">parser start<span class="punctuation">&#123;</span></span><br><span class="line">  return next_parser;</span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p>在每一个parser中都会依据目前所分析的内容决定下一个parser，直到回传的内容是ingress，或者其他control function，以ipv4为例：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">parser start<span class="punctuation">&#123;</span></span><br><span class="line">  return parser_ethernet;</span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">parser parser_ethernet<span class="punctuation">&#123;</span></span><br><span class="line">  extract(ethernet);</span><br><span class="line">  return select(ethernet.ether_type)<span class="punctuation">&#123;</span></span><br><span class="line">    <span class="number">0x0800</span> <span class="punctuation">:</span> parse_ipv4;</span><br><span class="line">    default <span class="punctuation">:</span> ingress;</span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">parser parser_ipv4<span class="punctuation">&#123;</span></span><br><span class="line">  extract(ipv4);</span><br><span class="line">  return ingress;</span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<ol>
<li><p>extract : 将packet以特定的header取出来，取出来的资料长度以header定义的为主。</p>
</li>
<li><p>retrun：透过return决定要往哪个parser或者control function，可以select或者return。</p>
</li>
<li><p>select(select_exp)：像c中switch-case，依据特定field值，取决定去那个parser。</p>
</li>
<li><p>select_exp：依据spec，可以是</p>
<p>field_ref : 如 ethernet.ether_type</p>
<p>latest.field_name : 以最後 extract 的 header 為主，取用他的 field</p>
<p>current (offset, length) : 以目前的 packet offset 位置開始某固定長度所取得的數值</p>
</li>
</ol>
<p>在 egress 階段開始時會先對 packet 做 deparsing 的動作，亦即將 ingress 所做出的修改（add header、modiffy header等）重新封裝回 packet 當中。</p>
<p><strong>P4-16</strong></p>
<p>與 1.0&#x2F;1.1 版不同的是，原先的版本需要定義出很多個 parser，並在不同的 parser 間切換，而新版的則是定義一個 parser，並在 parser 中撰寫很多不同的狀態（state），並透過 accept 與 reject 決定 parser 是否成功解析一個封包。</p>
<p>有限状态机</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">parser ParserName(packet_in pkt<span class="punctuation">,</span> ...) <span class="punctuation">&#123;</span></span><br><span class="line"></span><br><span class="line">    state start <span class="punctuation">&#123;</span></span><br><span class="line">        ....</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">    state state_name <span class="punctuation">&#123;</span></span><br><span class="line">        ....</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p>parser 所帶入的參數主要有：</p>
<ul>
<li>packet_in：執行期間所要解析的封包</li>
<li>其他輸出內容：可以是 struct 或是一般封包参照</li>
</ul>
<p>不同的 state 切換可以透過 <strong>transition</strong> 這個關鍵字去做切換，而 transition 後面可以接 state 名稱或是 select 語法，我們以 Ethernet + IPv4 為例子。</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">parser MyParser(packet_in pkt<span class="punctuation">,</span> out eth<span class="punctuation">,</span> out ipv4) <span class="punctuation">&#123;</span></span><br><span class="line"></span><br><span class="line">    state start <span class="punctuation">&#123;</span></span><br><span class="line">        transition eth;</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">    state eth <span class="punctuation">&#123;</span></span><br><span class="line">        pkt.extract(eth);</span><br><span class="line">        transition select(eth.ethType) <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="number">0x800</span><span class="punctuation">:</span> ip;</span><br><span class="line">            _<span class="punctuation">:</span> reject;</span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">    state ip <span class="punctuation">&#123;</span></span><br><span class="line">        pkt.extract(ipv4);</span><br><span class="line">        transition accept;</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p>除了使用 transition, select 以及 extract 以外，Parser 還有提供 verify 語法，透過 verify 檢查，決定 parser 是否要丟出 error 並跳至 reject 狀態，使用方法如下：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">verify(判斷式<span class="punctuation">,</span> 錯誤類別);</span><br></pre></td></tr></table></figure>

<h2 id="Table-Match-Action"><a href="#Table-Match-Action" class="headerlink" title="Table (Match + Action)"></a>Table (Match + Action)</h2><p>简单IPv4转发表的定义可以实现如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">table routing_table &#123;</span><br><span class="line">    key = &#123;</span><br><span class="line">        hdr.ipv4.dstAddr: lpm;</span><br><span class="line">    &#125;</span><br><span class="line">    actions = &#123;</span><br><span class="line">       ipv4_forward;</span><br><span class="line">       drop;</span><br><span class="line">       NoAction;</span><br><span class="line">    &#125;</span><br><span class="line">    default_action = NoAction();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的routing_table读取IPv4目标IP地址，并根据最长前缀匹配算法进行匹配。然后，在符合规则的数据包上，可以执行三个操作：ipv4_forward，drop或NoAction。</p>
<h2 id="Deparser"><a href="#Deparser" class="headerlink" title="Deparser"></a>Deparser</h2><p>它定义传出数据包的数据包标头顺序。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">control deparser(packet_out packet,</span><br><span class="line">                 in headers hdr) &#123;</span><br><span class="line"></span><br><span class="line">    apply &#123;</span><br><span class="line">        packet.emit(hdr.ethernet);</span><br><span class="line">        packet.emit(hdr.ipv4);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>完整的ipv4转发定义：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* -*- P4_16 -*- */</span></span><br><span class="line">#include &lt;core.p4&gt;</span><br><span class="line">#include &lt;v1model.p4&gt;</span><br><span class="line"></span><br><span class="line">const bit&lt;<span class="number">16</span>&gt; TYPE_IPV4 = <span class="number">0x0800</span>;</span><br><span class="line"></span><br><span class="line">typedef bit&lt;<span class="number">9</span>&gt;  egressSpec_t;</span><br><span class="line">typedef bit&lt;<span class="number">48</span>&gt; macAddr_t;</span><br><span class="line">typedef bit&lt;<span class="number">32</span>&gt; ip4Addr_t;</span><br><span class="line"></span><br><span class="line">header ethernet_t <span class="punctuation">&#123;</span></span><br><span class="line">    macAddr_t dstAddr;</span><br><span class="line">    macAddr_t srcAddr;</span><br><span class="line">    bit&lt;<span class="number">16</span>&gt;   etherType;</span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">header ipv4_t <span class="punctuation">&#123;</span></span><br><span class="line">    bit&lt;<span class="number">4</span>&gt;    version;</span><br><span class="line">    bit&lt;<span class="number">4</span>&gt;    ihl;</span><br><span class="line">    bit&lt;<span class="number">8</span>&gt;    diffserv;</span><br><span class="line">    bit&lt;<span class="number">16</span>&gt;   totalLen;</span><br><span class="line">    bit&lt;<span class="number">16</span>&gt;   identification;</span><br><span class="line">    bit&lt;<span class="number">3</span>&gt;    flags;</span><br><span class="line">    bit&lt;<span class="number">13</span>&gt;   fragOffset;</span><br><span class="line">    bit&lt;<span class="number">8</span>&gt;    ttl;</span><br><span class="line">    bit&lt;<span class="number">8</span>&gt;    protocol;</span><br><span class="line">    bit&lt;<span class="number">16</span>&gt;   hdrChecksum;</span><br><span class="line">    ip4Addr_t srcAddr;</span><br><span class="line">    ip4Addr_t dstAddr;</span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">struct headers <span class="punctuation">&#123;</span></span><br><span class="line">    ethernet_t   ethernet;</span><br><span class="line">    ipv4_t       ipv4;</span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">struct routing_metadata_t <span class="punctuation">&#123;</span></span><br><span class="line">    ip4Addr_t nhop_ipv4;</span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">struct metadata <span class="punctuation">&#123;</span></span><br><span class="line">    routing_metadata_t routing;</span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">parser RouterParser(packet_in packet<span class="punctuation">,</span></span><br><span class="line">                    out headers hdr<span class="punctuation">,</span></span><br><span class="line">                    inout metadata meta<span class="punctuation">,</span></span><br><span class="line">                    inout standard_metadata_t standard_metadata) <span class="punctuation">&#123;</span></span><br><span class="line"></span><br><span class="line">    state start <span class="punctuation">&#123;</span></span><br><span class="line">        transition parse_ethernet;</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">     state parse_ethernet <span class="punctuation">&#123;</span></span><br><span class="line">        packet.extract(hdr.ethernet);</span><br><span class="line">        transition select(hdr.ethernet.etherType) <span class="punctuation">&#123;</span></span><br><span class="line">            TYPE_IPV4<span class="punctuation">:</span> parse_ipv4;</span><br><span class="line">            default<span class="punctuation">:</span> accept;</span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">    state parse_ipv4 <span class="punctuation">&#123;</span></span><br><span class="line">        packet.extract(hdr.ipv4);</span><br><span class="line">        transition accept;</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">action drop() <span class="punctuation">&#123;</span></span><br><span class="line">    mark_to_drop();</span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">control ingress(inout headers hdr<span class="punctuation">,</span></span><br><span class="line">                inout metadata meta<span class="punctuation">,</span></span><br><span class="line">                inout standard_metadata_t standard_metadata) <span class="punctuation">&#123;</span></span><br><span class="line"></span><br><span class="line">    action ipv4_forward(ip4Addr_t nextHop<span class="punctuation">,</span> egressSpec_t port) <span class="punctuation">&#123;</span></span><br><span class="line">        meta.routing.nhop_ipv4 = nextHop;</span><br><span class="line">        standard_metadata.egress_spec = port;</span><br><span class="line">        hdr.ipv4.ttl = hdr.ipv4.ttl - <span class="number">1</span>;</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">    table routing_table <span class="punctuation">&#123;</span></span><br><span class="line">        key = <span class="punctuation">&#123;</span></span><br><span class="line">            hdr.ipv4.dstAddr<span class="punctuation">:</span> lpm;</span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">        actions = <span class="punctuation">&#123;</span></span><br><span class="line">            ipv4_forward;</span><br><span class="line">            drop;</span><br><span class="line">            NoAction;</span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">        default_action = NoAction();</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">    apply <span class="punctuation">&#123;</span></span><br><span class="line">        routing_table.apply();</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">control egress(inout headers hdr<span class="punctuation">,</span></span><br><span class="line">               inout metadata meta<span class="punctuation">,</span></span><br><span class="line">               inout standard_metadata_t standard_metadata) <span class="punctuation">&#123;</span></span><br><span class="line"></span><br><span class="line">    action set_dmac(macAddr_t dstAddr) <span class="punctuation">&#123;</span></span><br><span class="line">        hdr.ethernet.dstAddr = dstAddr;</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">    action set_smac(macAddr_t mac) <span class="punctuation">&#123;</span></span><br><span class="line">        hdr.ethernet.srcAddr = mac;</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">    table switching_table <span class="punctuation">&#123;</span></span><br><span class="line">        key = <span class="punctuation">&#123;</span></span><br><span class="line">            meta.routing.nhop_ipv4 <span class="punctuation">:</span> exact;</span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">        actions = <span class="punctuation">&#123;</span></span><br><span class="line">            set_dmac;</span><br><span class="line">            drop;</span><br><span class="line">            NoAction;</span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">        default_action = NoAction();</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">    table mac_rewriting_table <span class="punctuation">&#123;</span></span><br><span class="line"></span><br><span class="line">        key = <span class="punctuation">&#123;</span></span><br><span class="line">            standard_metadata.egress_port<span class="punctuation">:</span> exact;</span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">        actions = <span class="punctuation">&#123;</span></span><br><span class="line">            set_smac;</span><br><span class="line">            drop;</span><br><span class="line">            NoAction;</span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">        default_action = drop();</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">    apply <span class="punctuation">&#123;</span></span><br><span class="line">        switching_table.apply();</span><br><span class="line">        mac_rewriting_table.apply();</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">control deparser(packet_out packet<span class="punctuation">,</span></span><br><span class="line">                 in headers hdr) <span class="punctuation">&#123;</span></span><br><span class="line"></span><br><span class="line">    apply <span class="punctuation">&#123;</span></span><br><span class="line">        packet.emit(hdr.ethernet);</span><br><span class="line">        packet.emit(hdr.ipv4);</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">control MyComputeChecksum(inout headers hdr<span class="punctuation">,</span> inout metadata meta) <span class="punctuation">&#123;</span></span><br><span class="line">     apply <span class="punctuation">&#123;</span></span><br><span class="line">	update_checksum(</span><br><span class="line">	    hdr.ipv4.isValid()<span class="punctuation">,</span></span><br><span class="line">            <span class="punctuation">&#123;</span> hdr.ipv4.version<span class="punctuation">,</span></span><br><span class="line">	      hdr.ipv4.ihl<span class="punctuation">,</span></span><br><span class="line">              hdr.ipv4.diffserv<span class="punctuation">,</span></span><br><span class="line">              hdr.ipv4.totalLen<span class="punctuation">,</span></span><br><span class="line">              hdr.ipv4.identification<span class="punctuation">,</span></span><br><span class="line">              hdr.ipv4.flags<span class="punctuation">,</span></span><br><span class="line">              hdr.ipv4.fragOffset<span class="punctuation">,</span></span><br><span class="line">              hdr.ipv4.ttl<span class="punctuation">,</span></span><br><span class="line">              hdr.ipv4.protocol<span class="punctuation">,</span></span><br><span class="line">              hdr.ipv4.srcAddr<span class="punctuation">,</span></span><br><span class="line">              hdr.ipv4.dstAddr <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            hdr.ipv4.hdrChecksum<span class="punctuation">,</span></span><br><span class="line">            HashAlgorithm.csum16);</span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line">control MyVerifyChecksum(inout headers hdr<span class="punctuation">,</span> inout metadata meta) <span class="punctuation">&#123;</span></span><br><span class="line">    apply <span class="punctuation">&#123;</span>  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">V1Switch(</span><br><span class="line">RouterParser()<span class="punctuation">,</span></span><br><span class="line">MyVerifyChecksum()<span class="punctuation">,</span></span><br><span class="line">ingress()<span class="punctuation">,</span></span><br><span class="line">egress()<span class="punctuation">,</span></span><br><span class="line">MyComputeChecksum()<span class="punctuation">,</span></span><br><span class="line">deparser()</span><br><span class="line">) main;</span><br></pre></td></tr></table></figure>

<hr>
<h1 id="P4编译的个人理解"><a href="#P4编译的个人理解" class="headerlink" title="P4编译的个人理解"></a>P4编译的个人理解</h1><p>p4-hlir项目解析p4，可以看成p4语言的python解析器，生成对应的python对象。<br>基于这个解析结果可以再加上模板输出json或者c, 或者RTL，或者DPDK，或者NP的微码。。</p>
<p>BMv2可以看成是一个设备或者模拟器，p4c-bm就是调用p4-hlir解析p4，生成BMv2配置所需的json文件和远程调用接口。所以一个p4模型被mininet模拟的picture是这样的：</p>
<p>p4 -&gt; p4-hlir -&gt; p4c-bm -&gt; BMv2 -&gt; mininet</p>
<p>p4c-bm安装会把p4-hlir一并安装：<br>git clone <a target="_blank" rel="noopener" href="https://github.com/p4lang/p4c-bm.git">https://github.com/p4lang/p4c-bm.git</a><br>cd p4c-bm<br>sudo pip install -r requirements_v1_1.txt<br>sudo python setup.py install<br>至此&#x2F;usr&#x2F;local&#x2F;bin下面就有了p4开头的编译、校验、图片生成命令。</p>
<p>p4-validate用来检查p4语法</p>
<p>p4-shell simple_router.p4<br>这是python界面，h对象包含了HLIR已经解析出的对象，用法参见<a target="_blank" rel="noopener" href="https://github.com/p4lang/p4-hlir">https://github.com/p4lang/p4-hlir</a></p>
<p>p4-graph，这个项目不见了，原来跑到这里。这个命令会在当前目录下生成几个图片来描述Table&#x2F;parser关系。</p>
<p>p4c-bmv2<br>–json 生成P4的Json格式<br>–pd 生成 PD C++ code<br>–p4-v1.1 if your input program is a P4 v1.1 program</p>
<p>进入p4-factory的targets&#x2F;simple_router&#x2F;p4src，编译一下p4模型:<br>p4c-bmv2 –json JSON simple_router.p4<br>这时候JSON文件就生成了，打开看看是不是很眼熟？</p>
<ol>
<li>metadata跟header_type放在一起</li>
<li>headers成员里面有个metadata:true&#x2F;false来标记</li>
<li>headers里面还有standard_metadata</li>
<li>还有parsers&#x2F;actions组。。。</li>
</ol>
<p>至于怎么用BMv2或者mininet跑起来请看下一篇</p>
<p>如果你有自己的设备，CPU&#x2F;FPGA&#x2F;NP或者其它想支持P4的设备，可以用p4-hlir用python解析生成代码，或者基于p4c-bmv2输出的JSON生成代码或者执行文件。</p>
<p>BMv2j记得不是生成代码，而是加载JSON作为配置，如果代码效率要求不高应该可以这么用。</p>
<p>BTW，P4这个名字本来是Perforce的缩写，一个版本管理工具，P4全名是Programming Protocol independent Packet Processing, PPiPP更唯一</p>
<hr>
<p>为此，P4 语言定义了一套抽象转发模型[10]来支撑上诉三点语言特性。如图1 所示，抽象转发模型包含3 个主要部分。</p>
<p><img src="https://pic.downk.cc/item/5f562e2c160a154a676360ea.png"></p>
<ol>
<li>第一部分是可编程的数据报文头部解析器。开发人员在编写P4 代码时可以自定义报文头部解析流程，灵活解析不同的数据报文格式，经过编译之后产生类似于图2 所示的数据报文头部解析状态转移图，在部署时配置到可编程设备的报文头部解析器上。在数据报文进入可编程设备时数据平面先将报文头部和载荷分离，接着根据解析图的状态转移规则解析并保存报文的头部到对应自定义的头部域中，用于流水线中流表的匹配操作。</li>
</ol>
<p><img src="https://pic.downk.cc/item/5f562e4f160a154a676367af.png"></p>
<ol start="2">
<li><p>第二部分是可编程的多阶段流水线[10]。从图1 中可以看出主要分为入口流水线和出口流水线，其中，入口流水线主要进行数据分组修改以及决定出端口的操作，随之数据分组进入对应出端口的缓存队列中；而出口流水线仅负责数据分组的修改。开发人员可以自定义每张匹配动作表中的匹配头部域，执行动作及其参数，流表的数量等以及各条流水线中每张匹配动作表的执行顺序。P4 代码经过编译之后会产生一张由匹配动作表组成的有向无环图（DAG），即数据平面控制流。运行时数据平面会依据控制流中匹配动作表的顺序依次匹配处理每个数据报文。</p>
</li>
<li><p>第三部分是控制平面上的控制程序。P4 程序会在编译后生成对应的控制接口，主要负责在运行时向设备的数据平面下发并安装具体的流表匹配规则，配置计数器、寄存器等与平台相关的特定对象以及采集其他运行时的状态统计信息。</p>
</li>
</ol>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_34039018/article/details/88734481">引用</a></p>

        </div>

        
            <section class="post-copyright">
                
                    <p class="copyright-item">
                        <span>Author:</span>
                        <span>Ian Tang</span>
                    </p>
                
                
                    <p class="copyright-item">
                        <span>Permalink:</span>
                        <span><a href="https://tkhfree.github.io/2022/11/27/P4%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A8-2022-11-27/">https://tkhfree.github.io/2022/11/27/P4%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A8-2022-11-27/</a></span>
                    </p>
                
                
                    <p class="copyright-item">
                        <span>License:</span>
                        <span>Copyright (c) 2019 <a target="_blank" rel="noopener" href="http://creativecommons.org/licenses/by-nc/4.0/">CC-BY-NC-4.0</a> LICENSE</span>
                    </p>
                
                
                     <p class="copyright-item">
                         <span>Slogan:</span>
                         <span>Do you believe in <strong>DESTINY</strong>?</span>
                     </p>
                

            </section>
        
        <section class="post-tags">
            <div>
                <span>Tag(s):</span>
                <span class="tag">
                    
                    
                        <a href="/tags/ONOS/"># ONOS</a>
                    
                        <a href="/tags/network/"># network</a>
                    
                        
                </span>
            </div>
            <div>
                <a href="javascript:window.history.back();">back</a>
                <span>· </span>
                <a href="/">home</a>
            </div>
        </section>
        <section class="post-nav">
            
                <a class="prev" rel="prev" href="/2022/11/27/gRPC%E5%92%8Cprotobuf%E4%BB%8B%E7%BB%8D-2022-11-27/">gRPC和protobuf介绍</a>
            
            
            <a class="next" rel="next" href="/2022/11/27/ONOS+mininet-2022-11-27/">ONOS+mininet</a>
            
        </section>


    </article>
</div>

            </div>
            <footer id="footer" class="footer">
    <div class="copyright">
        <span>© Ian Tang | Powered by <a href="https://hexo.io" target="_blank">Hexo</a> & <a href="https://github.com/Siricee/hexo-theme-Chic" target="_blank">Chic</a></span>
    </div>
</footer>

    </div>
</body>

</html>